using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using DTT.AreaOfEffectRegions;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GameAOT;
using IQIGame.Onigao.Logic;
using LFloatMath.Math;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    /// <summary>
    /// 控制右下角技能图标与技能目标指向器
    /// </summary>
    public class SkillViewComponent : TComponent
    {
        public UnitView unit;
        private UnitViewComponent view;

        private BattleSceneEntityView curIndicator;
        private BattleSceneEntityView curIndicatorRange;
        // 被选中指示特效
        private BattleSceneEntityView selectedSign;
        public Transform indicatorTrans => this.curIndicator?.transform;
        public Transform indicatorRangeTrans => this.curIndicatorRange?.transform;

        public List<int> allSkills = new List<int>();
        private Dictionary<int, int> skillsCD = new Dictionary<int, int>();
        private Dictionary<int, int> skillCost = new Dictionary<int, int>(); // 技能消耗修改
        /// <summary>
        /// 槽位对应技能 注意不是全量
        /// </summary>
        public int[] skills = new int[BattleConst.SkillCastNum];

        public int curSkill { get; private set; }
        private TargetSelectConfig curSelectConfig;
        /// <summary>
        /// 单人指示器选中的目标
        /// </summary>
        public UnitView selectUnit;

        /// <summary>
        /// 是否展示指示器
        /// </summary>
        private bool showIndicator;
        /// <summary>
        /// 指示器摇杆转换为世界方向
        /// </summary>
        private Vector3 indicatorDir;
        /// 指示器移动范围
        private float centerRange;
        // 指示器碰撞ID
        public uint collisionID;


        /// <summary>
        /// 是否被其他人的技能选中
        /// </summary>
        public int selectedCollision { private get; set; } = -1;

        private int selectedSingle { get; set; } = -1;
        private int selected => this.selectedCollision >= 0 ? this.selectedCollision : this.selectedSingle;
        private int lastSelected = -1;

        protected override void OnDispose()
        {
        }

        public override void OnCreate(TEntity entity)
        {
            this.unit = entity as UnitView;
        }

        public void Initialize(IEnumerable<int> rSkillList)
        {
            this.allSkills.AddRange(rSkillList);
            this.view = this.unit.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
        }

        public void OnEnable()
        {
            this.ResetSkillCastList();
        }

        public void ResetSkillCastList()
        {
            //if (this.self.unitType == UnitType.Hero)
            {
                for (int i = 0; i < this.skills.Length; i++)
                {
                    this.skills[i] = 0;
                }

                for (int i = 0; i < this.allSkills.Count; i++)
                {
                    var nSkillID = this.allSkills[i];
                    var rSkillTab = TableCenter.skill.Get(nSkillID);
                    if (rSkillTab == null)
                    {
                        continue;
                    }

                    var nSlotIndex = rSkillTab.SkillSlot - 1;
                    var nPriority = rSkillTab.SkillSlotPriiority;
                    if (nSlotIndex >= 0 && nSlotIndex < this.skills.Length)
                    {
                        var nOldSkill = this.skills[nSlotIndex];
                        var nOldPriority = -1;
                        if (nOldSkill > 0)
                        {
                            var rOldConfig = TableCenter.skill.Get(nOldSkill);
                            nOldPriority = rOldConfig.SkillSlotPriiority;
                        }
                        if (nOldPriority < nPriority)
                        {
                            this.skills[nSlotIndex] = nSkillID;
                        }
                    }
                }
                
                var rPetComp = this.unit.GetComponent<PetViewComponent>(ETComponentType.PetViewComponent);
                if (rPetComp != null && rPetComp.petSkill != 0)
                {
                    this.skills[4] = rPetComp.petSkill;
                }
            }
        }

        public override void OnUpdate(float deltaTime)
        {
            base.OnUpdate(deltaTime);
            if (this.showIndicator && this.indicatorTrans)
            {
                this.SyncIndicatorPosAndRot();
            }

            var rViewComp = this.unit.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
            if (this.lastSelected != this.selected)
            {
                if (this.selected >= 0)
                {
                    if (this.selectedSign != null)
                    {
                        this.selectedSign.Put();
                        this.selectedSign = null;
                    }
                    if (this.selectedSign == null)
                    {
                        var rSelectRelation = BattleUtil.CheckTeamRelation(this.unit.teamIndex, this.selected);
                        var rSignName = rSelectRelation == ETargetRelation.Enemy ? "Selected" : "SelectedFriend";
                        this.selectedSign = ManagerCenter.Entity.Get(PathConstant.GetBattleCommonPrefabPath(rSignName), (int)EntityPoolGroupType.BattleSceneEntity) as BattleSceneEntityView;
                        this.LoadSelected().Forget();
                    }
                    rViewComp.OnSkillSelected();
                }
                else if (this.selected < 0)
                {
                    if (this.selectedSign?.isLoaded ?? false)
                    {
                        this.selectedSign.Put();
                        this.selectedSign = null;
                    }
                    rViewComp.OnStopSkillSelected();
                }
                this.lastSelected = this.selected;
            }
            if (this.selectedSign?.gameObject)
            {
                this.selectedSign.gameObject.transform.position = rViewComp.position;
            }
        }

#region 目标选择表现

        public void ShowSelect(int nSkillID, Vector3 dir, bool bSkillMoveing)
        {
            if (nSkillID == 0)
            {
                return;
            }

            if (this.curSkill != nSkillID)
            {
                var rTabConfig = TableCenter.skill.Get(nSkillID);
                switch (rTabConfig.CameraType)
                {
                    case 1:
                        BattleManager.Instance.ctrl.sceneCtrl.SetCameraState(BattleRenderSceneController.EBattleCameraState.Mid);
                        break;
                    case 2:
                        BattleManager.Instance.ctrl.sceneCtrl.SetCameraState(BattleRenderSceneController.EBattleCameraState.Far);
                        break;
                    default:
                        BattleManager.Instance.ctrl.sceneCtrl.SetCameraState(BattleRenderSceneController.EBattleCameraState.Main);
                        break;
                }

                // Load
                var rConfig = SkillFactory.GetConfig(nSkillID);
                var rMainPart = rConfig.parts.Find((x) => x.partType == (int)ESkillPartType.Main);
                if (rMainPart != null && rMainPart.effectives.Count > 0)
                {
                    this.curSkill = nSkillID;
                    var rSelect = rMainPart.effectives[0].target;
                    var rIndicatorName = rSelect.indicator;
                    if (string.IsNullOrEmpty(rIndicatorName))
                    {
                        rIndicatorName = TableCenter.skill.Get(nSkillID).Indicator;
                    }

                    if (string.IsNullOrEmpty(rIndicatorName))
                    {
                        return;
                    }
                    this.curSelectConfig = rSelect;
                    this.showIndicator = true;
                    this.LoadIndicator(rIndicatorName, dir, rSelect).Forget();
                }
            }
            else if (this.curIndicator != null)
            {
                // 设置参数
                this.SetIndicatorParam(dir, this.curSelectConfig, bSkillMoveing);
            }
        }

        public void CancelSelect(int nSkillID)
        {
            if (nSkillID == this.curSkill)
            {
                this.showIndicator = false;
                this.curSkill = 0;
                if (this.curIndicator != null && this.curIndicator.isLoaded)
                {
                    this.curIndicator.Put();
                    this.curIndicator = null;
                }
                if (this.curIndicatorRange != null && this.curIndicatorRange.isLoaded)
                {
                    this.curIndicatorRange.Put();
                    this.curIndicatorRange = null;
                }
            }
            BattleManager.Instance.ctrl.collisionCtrl.RemoveTempCollision(this.collisionID);
            // 移除选中效果
            // for (int i = 0; i < this.selectedUnitCache.Count; i++)
            // {
            //     var rComp = this.selectedUnitCache[i].GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
            //     rComp.selected = false;
            // }
            if (this.selectUnit != null)
            {
                var rComp = this.selectUnit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
                rComp.selectedSingle = -1;
                this.selectUnit = null;
            }
        }

        /// <summary>
        /// 设置技能目标指示器参数
        /// </summary>
        /// <param name="rIndicatorName"></param>
        /// <param name="dir"></param>
        /// <param name="rSelect"></param>
        private async UniTaskVoid LoadIndicator(string rIndicatorName, Vector3 dir, TargetSelectConfig rSelect)
        {
            if (rSelect == null)
            {
                return;
            }
            if (this.curIndicator != null)
            {
                this.curIndicator.Put();
            }
            this.curIndicator = ManagerCenter.Entity.Get(PathConstant.GetSkillSelectPath(rIndicatorName),
                (int)EntityPoolGroupType.BattleSceneEntity) as BattleSceneEntityView;
            await this.curIndicator.Load();
            if (!this.showIndicator && this.curIndicator != null)
            {
                this.curIndicator.Put();
                this.curIndicator = null;
                return;
            }
            this.curIndicator.transform.SetParent(BattleManager.Instance.ctrl.sceneCtrl.targetSelect);

            this.centerRange = 0f;
            switch ((ETargetSelectType)rSelect.type)
            {
                case ETargetSelectType.Circle:
                {
                    var rIndicator = this.curIndicator.gameObject.GetComponent<CircleRegion>();
                    this.curIndicator.comp = rIndicator;
                    if (rSelect.customConfig is TargetSelect_Circle_ConfigData rConfig)
                    {
                        rIndicator.Radius = new LFloat(true, rConfig.raduis).ToFloat();
                        this.centerRange = new LFloat(true, rConfig.centerRange).ToFloat();
                    }
                    break;
                }
                case ETargetSelectType.Fan:
                {
                    var rIndicator = this.curIndicator.gameObject.GetComponent<ArcRegion>();
                    this.curIndicator.comp = rIndicator;
                    if (rSelect.customConfig is TargetSelect_Fan_ConfigData rConfig)
                    {
                        rIndicator.Radius = new LFloat(true, rConfig.raduis).ToFloat();
                        rIndicator.Arc = new LFloat(true, rConfig.angle).ToFloat();
                        this.centerRange = new LFloat(true, rConfig.centerRange).ToFloat();
                    }
                    break;
                }
                case ETargetSelectType.Cub:
                {
                    var rIndicator = this.curIndicator.gameObject.GetComponent<LineRegion>();
                    this.curIndicator.comp = rIndicator;
                    if (rSelect.customConfig is TargetSelect_Cub_ConfigData rConfig)
                    {
                        rIndicator.Width = new LFloat(true, rConfig.width).ToFloat();
                        rIndicator.Length = new LFloat(true, rConfig.length).ToFloat();
                        this.centerRange = 0;
                    }
                    break;
                }
                case ETargetSelectType.InputUnit:
                {
                    var rIndicator = this.curIndicator.gameObject.GetComponent<LineToUnitRegion>();
                    this.curIndicator.comp = rIndicator;

                    if (rSelect.customConfig is TargetSelect_InputUnit_ConfigData rConfig)
                    {
                        this.centerRange = 0;
                        var rSelfBind = this.unit.GetComponent<BattleViewBindPointComponent>(ETComponentType.BattleViewBindPointComponent);
                        rIndicator.Initialize(rSelfBind.GetBindPoint(1), rConfig);
                    }

                    break;
                }
            }
            this.SetIndicatorParam(dir, rSelect, false);
            if (this.curIndicator.gameObject && this.curSkill != 0)
            {
                this.curIndicator.gameObject.SetActive(true);
            }
            if (this.centerRange > 0)
            {
                this.curIndicatorRange = ManagerCenter.Entity.Get(PathConstant.GetSkillSelectPath("SkillRange"),
                    (int)EntityPoolGroupType.BattleSceneEntity) as BattleSceneEntityView;
                await this.curIndicatorRange.Load();
                if (!this.showIndicator && this.curIndicatorRange != null)
                {
                    this.curIndicatorRange.Put();
                    this.curIndicatorRange = null;
                }
                else
                {
                    this.curIndicator.transform.SetParent(BattleManager.Instance.ctrl.sceneCtrl.targetSelect);
                    var rReg = this.curIndicatorRange.gameObject.GetComponent<CircleRegion>();
                    rReg.Radius = this.centerRange;
                }
            }
        }

        private void SetIndicatorParam(Vector3 dir, TargetSelectConfig rSelect, bool bSkillMoving)
        {
            this.indicatorDir = dir;
            switch ((ETargetSelectType)rSelect.type)
            {
                case ETargetSelectType.Circle:
                case ETargetSelectType.Fan:
                case ETargetSelectType.Cub:
                    var rCollisonCtrl = BattleManager.Instance.ctrl.collisionCtrl;
                    var rPos = this.view.position + dir * this.centerRange;
                    rCollisonCtrl.CreateIndicatorCollision(this.unit, rSelect, rPos, this.indicatorDir,
                        this.curIndicator, this.CheckCollisionCondition, ref this.collisionID);
                    break;
                case ETargetSelectType.InputUnit:
                    if (rSelect.customConfig is TargetSelect_InputUnit_ConfigData rConfig && this.curIndicator.comp is LineToUnitRegion rIndicator)
                    {
                        this.SyncIndicatorPosAndRot();
                        var rTargetPos = rIndicator.MaxLength * dir + rIndicator.transform.position;
                        var rUnits = BattleManager.Instance.ctrl.data.GetUnitByRelation(this.unit, (ETargetRelation)rSelect.targetRelation);
                        var fMinDis = -1f;
                        var nSelectIndex = -1;
                        var rSelfPos = this.view.position;

                        // InputUnit中，targetRange指自动锁定到角色时，角色的判定范围与半径的倍率
                        // 若未在移动状态，随便指个较大值让可以默认选中最近的角色
                        var fSqrTargetRange = bSkillMoving ? rIndicator.targetRange * rIndicator.targetRange : 100000;
                        for (int i = 0; i < rUnits.Count; i++)
                        {
                            var rUnit = rUnits[i];
                            if (rUnit.IsDead())
                            {
                                continue;
                            }
                            var rComp = rUnit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
                            rComp.selectedSingle = -1;
                            var rUnitPos = rUnit.pos.ToVector3XZ();
                            var fDis = (rUnitPos - rSelfPos).sqrMagnitude;
                            var fTarDis = (rUnitPos - rTargetPos).sqrMagnitude;
                            if (fDis <= rIndicator.MaxLength * rIndicator.MaxLength && (fMinDis <= 0 || fTarDis < fMinDis) && fTarDis <= fSqrTargetRange * rUnit.radius * rUnit.radius)
                            {
                                fMinDis = fTarDis;
                                nSelectIndex = i;
                            }
                        }
                        if (nSelectIndex >= 0)
                        {
                            var rSelectUnit = rUnits[nSelectIndex];
                            var rTargetBind = rSelectUnit.GetComponent<BattleViewBindPointComponent>(ETComponentType.BattleViewBindPointComponent);
                            rIndicator.LockTarget(rTargetBind.GetBindPoint(4));
                            rIndicator.LineTarget(dir);
                            this.selectUnit = rSelectUnit;
                            var rComp = this.selectUnit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
                            rComp.selectedSingle = this.unit.teamIndex;
                        }
                        else
                        {
                            rIndicator.LockTarget(null);
                            rIndicator.LineTarget(dir);
                            if (this.selectUnit != null)
                            {
                                var rComp = this.selectUnit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
                                rComp.selectedSingle = -1;
                                this.selectUnit = null;
                            }
                        }
                    }
                    break;
            }
        }

        private void SyncIndicatorPosAndRot()
        {
            var rPos = this.view.position;
            if (indicatorTrans)
            {
                if (this.indicatorDir != Vector3.zero)
                {
                    // 处理偏移
                    var rDis = this.indicatorDir * this.centerRange;
                    rPos += rDis;
                    this.indicatorTrans.rotation = Quaternion.LookRotation(this.indicatorDir);
                }

                this.indicatorTrans.localPosition = rPos;
            }
            if (indicatorRangeTrans)
            {
                this.indicatorRangeTrans.localPosition = this.view.position;
            }
        }

        private async UniTaskVoid LoadSelected()
        {
            if (this.selectedSign != null)
            {
                await this.selectedSign.Load();
                if (this.selected < 0)
                {
                    this.selectedSign.Put();
                    this.selectedSign = null;
                }
                else
                {
                    var rViewComp = this.unit.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
                    this.selectedSign.gameObject.transform.position = rViewComp.position;
                }
            }
        }

        private bool CheckCollisionCondition(CollisionViewComponent comp)
        {
            if (comp.unit.GetComponent<DeadViewComponent>(ETComponentType.DeadViewComponent) != null)
            {
                return false;
            }
            var rSelect = this.curSelectConfig;
            if (rSelect != null)
            {
                // 表现层队伍判断无法判断自身，需要分开
                if (this.unit != comp.unit)
                {
                    var rRelation = BattleUtil.CheckTeamRelation(this.unit.teamIndex, comp.unit.teamIndex);
                    return (rSelect.targetRelation & (int)rRelation) > 0;
                }
                else
                {
                    return (rSelect.targetRelation & (int)ETargetRelation.Self) > 0;
                }
            }
            return false;
        }

#endregion

#region 技能InputIcon

        public void AddSkill(int nSkillID)
        {
            if (!this.allSkills.Contains(nSkillID))
            {
                this.allSkills.Add(nSkillID);
            }

            var rConfig = TableCenter.skill.Get(nSkillID);
            var nSlot = rConfig.SkillSlot - 1;
            var nPriority = rConfig.SkillSlotPriiority;
            if (nSlot >= 0 && nSlot < this.skills.Length)
            {
                if (this.skills[nSlot] != 0)
                {
                    var nOldPriority = TableCenter.skill.Get(this.skills[nSlot]).SkillSlotPriiority;
                    if (nOldPriority < nPriority)
                    {
                        this.skills[nSlot] = nSkillID;
                    }
                }
                else
                {
                    this.skills[nSlot] = nSkillID;
                }
            }
        }

        public void RemoveSkill(int nSkillID)
        {
            var rSkillConfig = TableCenter.skill.Get(nSkillID);
            var nSlot = rSkillConfig?.SkillSlot - 1 ?? 0;
            var nMaxPriority = -1;
            if (this.allSkills.Remove(nSkillID) && nSlot >= 0 && nSlot < this.skills.Length && this.skills[nSlot] == nSkillID)
            {
                for (int i = 0; i < this.allSkills.Count; i++)
                {
                    var nCurSkill = this.allSkills[i];
                    var rConfig = TableCenter.skill.Get(nCurSkill);
                    if (rConfig.SkillSlot - 1 == nSlot && rConfig.SkillSlotPriiority > nMaxPriority)
                    {
                        this.skills[i] = nCurSkill;
                        nMaxPriority = rConfig.SkillSlotPriiority;
                    }
                }
            }
        }

        public void OnCast(int nSkillID)
        {
            var rSkillConfig = TableCenter.skill.Get(nSkillID);
            if (rSkillConfig.CoolDown > 0)
            {
                this.skillsCD[nSkillID] = rSkillConfig.CoolDown;
            }
        }

        private Dictionary<int, int> dictCache = new Dictionary<int, int>();

        public void OnRoundStart()
        {
            dictCache.Clear();
            dictCache.AddRange(this.skillsCD);
            foreach (var rPair in dictCache)
            {
                if (rPair.Value > 0)
                {
                    this.skillsCD[rPair.Key]--;
                }
                else
                {
                    this.skillsCD.Remove(rPair.Key);
                }
            }
        }

#endregion

        public int SkillSpCost(int skillID)
        {
            if (this.skillCost.TryGetValue(skillID, out var nCost))
            {
                return nCost;
            }
            var rSkillConfig = TableCenter.skill.Get(skillID);
            return rSkillConfig?.SPCost ?? 0;
        }

        public bool SkillCanCast(int nSkillID, out int nCD)
        {
            if (nSkillID == 0)
            {
                nCD = -1;
                return false;
            }
            var bCan = true;
            if (this.skillsCD.TryGetValue(nSkillID, out nCD))
            {
                bCan = nCD <= 0;
            }
            var rSkillTabConfig = TableCenter.skill.Get(nSkillID);
            var nCost = this.SkillSpCost(nSkillID);
            if (nCost > 0)
            {
                var rAttrComp = this.unit.GetComponent<AttributeViewComponent>(ETComponentType.AttributeViewComponent);
                var nCurSp = rAttrComp.Get(EAttributeType.SP);
                bCan &= nCost <= nCurSp;
            }
            var nCostFormula = rSkillTabConfig.CostLimit;
            if (nCostFormula > 0)
            {
                bCan = bCan && Formula.Calculate<bool>(nCostFormula, this.unit);
            }
            return bCan;
        }
    }
}